<!--------------------------------
 - 用户、角色、组织选择器
 - @Author: zuuuYao
 - @LastEditor: zuuuYao
 - @LastEditTime: 2025/09/07 23:23:31
 --------------------------------->
<script setup>
import { NAvatar, NButton, NDataTable, NIcon, NSwitch, NTag } from 'naive-ui'
import { ChevronForward } from '@vicons/ionicons5'
import { checkedType } from './index.js'
import api from '@/views/pms/organizational/api.js'
import { MeModal } from '@/components/index.js'
import userApi from '@/views/pms/user/api.js'
import roleApi from '@/views/pms/role/api'
import { formatDateTime } from '@/utils/index.js'
// 定义组件名称
defineOptions({ name: 'UserOrganizationSelector' })
// 定义组件属性
const props = defineProps({
  // 是否选择器禁用
  disabled: {
    type: Boolean,
    default: false,
  },
  // 是否多选
  multiple: {
    type: Boolean,
    default: false,
  },
  // 默认选中数据 [{ id: id, name: name, type: type}]
  checkedData: {
    type: Array,
    default: () => [],
  },
  // 选择范围允许选择的类型默认全部, 当multiple是false时只有第一个生效; [checkedType.user, checkedType.organization, checkedType.role]
  selectRange: {
    type: Array,
    default: () => [checkedType.user, checkedType.organization, checkedType.role],
  },
})
// 声明事件
const emit = defineEmits(['update'])
// 挂载函数
onMounted(() => {
  loadOrgTreeData()
  loadRole()
})
// 模态框ref
const modalRef = ref(null)
// 选中的用户和部门数据
const checkedDataRef = ref(props.checkedData || [])

// 组织树结构数据
const orgTreeData = ref([])
// 树当前选中节点
const selectedOrgNode = ref(null)
// 用户 Tab 树搜索关键词
const userTreeSearch = ref('')
// 部门 Tab 树搜索关键词
const orgTreeSearch = ref('')
// 防抖后的搜索关键词
const debouncedUserTreeSearch = ref('')
const debouncedOrgTreeSearch = ref('')
let userDebounceTimer = null
let orgDebounceTimer = null
watch(userTreeSearch, (val) => {
  clearTimeout(userDebounceTimer)
  userDebounceTimer = setTimeout(() => {
    debouncedUserTreeSearch.value = val
  }, 300)
})
watch(orgTreeSearch, (val) => {
  clearTimeout(orgDebounceTimer)
  orgDebounceTimer = setTimeout(() => {
    debouncedOrgTreeSearch.value = val
  }, 300)
})
// 树选中节点key
const selectedOrgKeys = ref(checkedDataRef.value.filter(item => item.type === checkedType.organization).map(item => item.id))
// 树设置项
const orgTreeOptions = ref({
  treeLoading: true,
  bodyStyle: {
    width: '600px',
  },
})

// 表格分页对象
const pagination = reactive({
  page: 1,
  pageSize: 20,
  pageSizes: [5, 20, 50, 100],
  showSizePicker: false,
  showQuickJumper: false,
  displayOrder: ['pages', 'quick-jumper', 'size-picker'],
  prefix({ itemCount }) {
    return `共 ${itemCount} 条数据`
  },
  onChange: (page) => {
    pagination.page = page
    loadUsersByOrg()
  },
  onUpdatePageSize: (pageSize) => {
    pagination.pageSize = pageSize
    loadUsersByOrg()
  },
})
// 表格选择的行key
const selectedUserRowKeys = ref(checkedDataRef.value.filter(item => item.type === checkedType.user).map(item => item.id))
const genderOptions = [
  { label: '男', value: 'MALE' },
  { label: '女', value: 'FEMALE' },
  { label: '保密', value: 'UNKNOWN' },
]
// 表格设置项
const userTableOptions = ref({
  columns:
    [
      {
        type: 'selection',
        multiple: props.multiple,
        disabled: ({ enable }) => enable === false,
      },
      {
        title: '头像',
        key: 'avatarUrl',
        render: ({ avatarUrl }) =>
          h(NAvatar, {
            size: 'medium',
            round: true,
            bordered: true,
            src: avatarUrl,
          }),
      },
      { title: '用户名', key: 'username', width: 100, ellipsis: { tooltip: true } },
      {
        title: '角色',
        key: 'roles',
        ellipsis: { tooltip: true },
        render: ({ roles }) => {
          if (roles?.length) {
            return roles.map((item, index) =>
              h(
                NTag,
                {
                  type: 'success',
                  round: true,
                  size: 'small',
                  bordered: false,
                  style: index > 0 ? 'margin-left: 8px;' : '',
                },
                { default: () => item.name },
              ),
            )
          }
          return '暂无角色'
        },
      },
      {
        title: '性别',
        key: 'gender',
        render: ({ gender }) => genderOptions.find(item => gender === item.value)?.label ?? '',
      },
      {
        title: '状态',
        key: 'enable',
        render: row =>
          h(
            NSwitch,
            {
              size: 'small',
              rubberBand: false,
              value: row.enable,
              disabled: true,
            },
            {
              checked: () => '启用',
              unchecked: () => '停用',
            },
          ),
      },
    ],
  loading: false,
})
// 表格数据
const userTableData = ref([])

/**
 * 加载当前选择节点的用户数据
 */
async function loadUsersByOrg() {
  // 未选中节点不查询
  if (!selectedOrgNode.value) {
    return
  }
  // 显示加载层
  userTableOptions.value.loading = true

  try {
    // 请求用户数据
    const res = await userApi.read({
      pageNo: pagination.page,
      pageSize: pagination.pageSize,
      organizationId: selectedOrgNode.value.id || '',
    })
    // 设置表格
    userTableData.value = res?.result?.records || []
    // 设置分页数据
    pagination.itemCount = res?.result?.total || 0
  }
  catch (error) {
    console.error('加载用户数据失败:', error)
    userTableData.value = []
    pagination.itemCount = 0
  }
  finally {
    // 关闭加载层
    userTableOptions.value.loading = false
  }
}

/**
 * 获取树数据
 * @returns {Promise<void>}
 */
async function loadOrgTreeData() {
  // 显示树加载层
  orgTreeOptions.value.treeLoading = true
  try {
    // 请求树结构数据
    const res = await api.organizationTree()
    orgTreeData.value = res?.result || []
  }
  catch (error) {
    console.error('加载组织架构失败:', error)
    orgTreeData.value = []
  }
  finally {
    // 关闭树加载层
    orgTreeOptions.value.treeLoading = false
  }
}

/**
 * 用户选择器选中树节点
 * @param keys
 * @param option
 * @param action
 * @param node
 */
function handleOrgTreeSelect(keys, option, { node }) {
  selectedOrgNode.value = node
  // 重置分页
  pagination.page = 1
  // 加载数据
  loadUsersByOrg()
}

/**
 * 树节点内容渲染函数
 * @param option
 * @returns {VNode}
 */
function renderOrgLabel(option) {
  // 组织禁用则禁止选择
  option.option.disabled = option.option.enable === false
  return h(
    'span',
    {
      style: option.option.enable ? '' : 'color: grey;',
    },
    {
      default: () => option.option.name,
    },
  )
}

/**
 * 部门选择器勾选节点发生变化时回调
 * @param keys 选中节点的 key 数组
 * @param option 选中选项数组
 * @param node 选中的节点对象
 */
function handleOrgSelectionChange(keys, option, { node }) {
  // 从 option 中提取新的选中数据
  const newOrganizationData = option.map(row => ({
    id: row.id,
    name: row.name,
    type: checkedType.organization,
  }))
  selectedOrgKeys.value = newOrganizationData.map(item => item.id)
  // 添加到 checkedDataRef 中
  upsertSelectedByType(checkedType.organization, newOrganizationData)
}

/**
 * 开关图标
 * @returns {VNode}
 */
function renderSwitcherIcon(treeOption) {
  if (treeOption.option.children && treeOption.option.children.length > 0) {
    return h(NIcon, null, { default: () => h(ChevronForward) })
  }
  else {
    // 不存在下级不展示开关图标
    return h(NIcon, null, { default: () => h() })
  }
}

/**
 * 表格选中回调
 */
function handleUserSelectionChange() {
  // 获取当前选中的行数据
  const selectedRows = userTableData.value.filter(row =>
    selectedUserRowKeys.value.includes(row.id),
  )
  // 从 selectedRows 中提取新的 type: 'USER' 数据
  const newUserData = selectedRows.map(row => ({
    id: row.id,
    name: row.username,
    type: checkedType.user,
  }))
  // 添加到 checkedDataRef 中
  upsertSelectedByType(checkedType.user, newUserData)
}

// 角色表格选择的行key
const selectedRoleRowKeys = ref(checkedDataRef.value.filter(item => item.type === checkedType.role).map(item => item.id))
// 角色表格设置项
const roleTableOptions = ref({
  columns:
    [
      {
        type: 'selection',
        multiple: props.multiple,
        disabled: ({ enable }) => enable === false,
      },
      {
        title: '图标',
        key: 'icon',
        render: ({ icon }) =>
          h(
            'i',
            {
              class: icon,
            },
          ),
      },
      { title: '角色名', key: 'name' },
      { title: '角色编码', key: 'code' },
      {
        title: '创建时间',
        key: 'createdTime',
        render(row) {
          return h('span', formatDateTime(row.createdTime))
        },
      },
      {
        title: '状态',
        key: 'enable',
        render: ({ enable }) =>
          h(
            NTag,
            {
              round: true,
              type: enable ? 'success' : 'error',
            },
            {
              default: () => enable ? '启用' : '停用',
            },
          ),
      },
      { title: '备注', key: 'remark' },
    ],
  pagination: {
    page: 1,
    pageSize: 20,
    pageSizes: [10, 20, 50, 100],
    showSizePicker: false,
    showQuickJumper: false,
    displayOrder: ['pages', 'quick-jumper', 'size-picker'],
    prefix({ itemCount }) {
      return `共 ${itemCount} 条数据`
    },
    onChange: (page) => {
      pagination.page = page
      loadRole()
    },
    onUpdatePageSize: (pageSize) => {
      pagination.pageSize = pageSize
      loadRole()
    },
  },
  loading: false,
})
// 角色表格数据
const roleTableData = ref([])

/**
 * 加载角色数据
 */
function loadRole() {
  roleApi.read({ pageNo: roleTableOptions.value.pagination.page, pageSize: roleTableOptions.value.pagination.pageSize })
    .then((res) => {
      roleTableData.value = res?.result?.records || []
      roleTableOptions.value.pagination.itemCount = res?.result?.total || 0
    }).catch(() => {
      roleTableData.value = []
      roleTableOptions.value.pagination.itemCount = 0
    })
}

/**
 * 处理角色表格勾选数据变化时
 */
function handleRoleSelectionChange() {
  // 获取当前选中的行数据
  const selectedRows = roleTableData.value.filter(row =>
    selectedRoleRowKeys.value.includes(row.id),
  )
  // 从 selectedRows 中提取新的 type: 'ROLE' 数据
  const newRoleData = selectedRows.map(row => ({
    id: row.id,
    name: row.name,
    type: checkedType.role,
  }))
  // 添加到 checkedDataRef 中
  upsertSelectedByType(checkedType.role, newRoleData)
}

/**
 * 添加选中数据
 * @param type  类型
 * @param data  数据
 */
function upsertSelectedByType(type, data) {
  // 清理 checkedDataRef 中原有的 type === type 数据
  const cleanedCheckedData = checkedDataRef.value.filter(item => item.type !== type)
  // 合并清理后的旧数据 + 新数据
  checkedDataRef.value = [...cleanedCheckedData, ...data]
  // 发送事件
  emit('update', checkedDataRef.value)
}
/**
 * 移除选中数据
 * @param id  id
 */
function removeSelectedById(id) {
  // 移除 checkedRowKeysRef.value 中 id === id 的数据
  selectedUserRowKeys.value = selectedUserRowKeys.value.filter(item => item !== id)
  // 移除 checkedTreeKey.value 中 id === id 的数据
  selectedOrgKeys.value = selectedOrgKeys.value.filter(item => item !== id)
  // 清理 checkedDataRef 中原有的 id === id 数据
  checkedDataRef.value = checkedDataRef.value.filter(item => item.id !== id)
  // 发送事件
  emit('update', checkedDataRef.value)
}
/**
 * 根据类型获取选中数据
 * @param type 类型
 * @returns {UnwrapRefSimple<*>[]}
 */
function getSelectedByType(type) {
  return checkedDataRef.value.filter(item => item.type === type)
}
defineExpose({
  checkedType,
})
</script>

<template>
  <n-flex class="w-80vh">
    <NButton type="info" class="pl-15 pr-15" size="small" :disabled="props.disabled" strong secondary @click="modalRef.open()">
      添加
    </NButton>
    <n-space class="h-7vh overflow-y-auto">
      <n-space>
        <TransitionGroup name="list" tag="ul">
          <NTag v-for="item in getSelectedByType(checkedType.user)" :key="item.id" closable type="success" size="small" :bordered="false" class="mb-6 mr-6" @close="removeSelectedById(item.id)">
            {{ item.name }}
          </NTag>
        </TransitionGroup>
      </n-space>
      <n-space>
        <TransitionGroup name="list" tag="ul">
          <NTag v-for="item in getSelectedByType(checkedType.role)" :key="item.id" closable type="warning" size="small" :bordered="false" class="mb-6 mr-6" @close="removeSelectedById(item.id)">
            {{ item.name }}
          </NTag>
        </TransitionGroup>
      </n-space>
      <n-space>
        <TransitionGroup name="list" tag="ul">
          <NTag v-for="item in getSelectedByType(checkedType.organization)" :key="item.id" closable type="info" size="small" :bordered="false" class="mb-6 mr-6" @close="removeSelectedById(item.id)">
            {{ item.name }}
          </NTag>
        </TransitionGroup>
      </n-space>
    </n-space>
  </n-flex>
  <MeModal ref="modalRef" width="1000px">
    <n-space class="h-7vh overflow-y-auto">
      <n-space>
        <TransitionGroup name="list" tag="ul">
          <NTag v-for="item in getSelectedByType(checkedType.user)" :key="item.id" closable type="success" size="small" :bordered="false" class="mb-6 mr-6" @close="removeSelectedById(item.id)">
            {{ item.name }}
          </NTag>
        </TransitionGroup>
      </n-space>
      <n-space>
        <TransitionGroup name="list" tag="ul">
          <NTag v-for="item in getSelectedByType(checkedType.role)" :key="item.id" closable type="warning" size="small" :bordered="false" class="mb-6 mr-6" @close="removeSelectedById(item.id)">
            {{ item.name }}
          </NTag>
        </TransitionGroup>
      </n-space>
      <n-space>
        <TransitionGroup name="list" tag="ul">
          <NTag v-for="item in getSelectedByType(checkedType.organization)" :key="item.id" closable type="info" size="small" :bordered="false" class="mb-6 mr-6" @close="removeSelectedById(item.id)">
            {{ item.name }}
          </NTag>
        </TransitionGroup>
      </n-space>
    </n-space>
    <n-tabs type="line" class="h-70vh" animated>
      <n-tab-pane name="user" tab="用户" :disabled="!props.selectRange.includes(checkedType.user)">
        <n-flex justify="space-between">
          <n-flex vertical :size="[46, 10]">
            <h3>组织架构</h3>
            <div class="flex">
              <n-input v-model:value="userTreeSearch" placeholder="搜索" clearable />
              <NButton class="ml-12" type="primary" ghost quaternary @click="loadOrgTreeData">
                <i class="i-fe:rotate-ccw mr-4 text-14" />
              </NButton>
            </div>
            <n-spin size="small" :show="orgTreeOptions.treeLoading">
              <n-tree
                class="hide-scrollbar h-70vh overflow-y-auto"
                :show-irrelevant-nodes="false"
                :pattern="debouncedUserTreeSearch"
                :data="orgTreeData"
                :selected-keys="[selectedOrgNode?.id]"
                :render-switcher-icon="renderSwitcherIcon"
                :render-label="renderOrgLabel"
                :on-update:selected-keys="handleOrgTreeSelect"
                key-field="id"
                label-field="name"

                block-line default-expand-all
              />
            </n-spin>
          </n-flex>
          <n-flex vertical class="hide-scrollbar ml-20 h-80vh flex-1 overflow-y-auto" style="flex-grow: 2">
            <n-flex inline justify="space-between">
              <h3>{{ selectedOrgNode?.name }}</h3>
            </n-flex>
            <NDataTable
              v-model:checked-row-keys="selectedUserRowKeys"
              :row-key="row => row.id"
              :remote="true"
              :columns="userTableOptions.columns"
              :loading="userTableOptions.loading"
              :data="userTableData"
              :pagination="pagination"
              :max-height="560"
              virtual-scroll
              @update:checked-row-keys="handleUserSelectionChange"
            />
          </n-flex>
        </n-flex>
      </n-tab-pane>
      <n-tab-pane name="role" tab="角色" :disabled="!props.selectRange.includes(checkedType.role)">
        <NDataTable
          v-model:checked-row-keys="selectedRoleRowKeys"
          :row-key="row => row.id"
          :remote="true"
          :columns="roleTableOptions.columns"
          :loading="roleTableOptions.loading"
          :data="roleTableData"
          :pagination="roleTableOptions.pagination"
          :max-height="560"
          virtual-scroll
          @update:checked-row-keys="handleRoleSelectionChange"
        />
      </n-tab-pane>
      <n-tab-pane name="organization" tab="组织" :disabled="!props.selectRange.includes(checkedType.organization)">
        <n-flex vertical :size="[46, 10]">
          <h3>组织选择</h3>
          <div class="flex">
            <n-input v-model:value="orgTreeSearch" placeholder="搜索" clearable />
            <NButton class="ml-12" type="primary" ghost quaternary @click="loadOrgTreeData">
              <i class="i-fe:rotate-ccw mr-4 text-14" />
            </NButton>
          </div>
          <n-spin size="small" :show="orgTreeOptions.treeLoading">
            <n-tree
              :multiple="props.multiple"
              :checked-keys="selectedOrgKeys"
              class="hide-scrollbar h-70vh overflow-y-auto"
              :show-irrelevant-nodes="false"
              :pattern="debouncedOrgTreeSearch"
              :data="orgTreeData"
              :render-label="renderOrgLabel"
              :render-switcher-icon="renderSwitcherIcon"
              :on-update:checked-keys="handleOrgSelectionChange"
              key-field="id"
              label-field="name"
              default-expand-all
              block-line
              checkable
            />
          </n-spin>
        </n-flex>
      </n-tab-pane>
    </n-tabs>
  </MeModal>
</template>

<style scoped>
.list-enter-active,
.list-leave-active {
  transition: all 0.5s ease;
}
.list-enter-from,
.list-leave-to {
  opacity: 0;
  transform: translateX(30px);
}
</style>
